#pragma once

/*
    Copyright (C) 2010, Felix Bytow <drako123@gmx.de>

    This file is part of DBCpp.

    DBCpp is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DBCpp is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DBCpp.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef __DBCpp_HPP__
#define __DBCpp_HPP__

#include <vector>
#include <fstream>
#include <sstream>
#include <map>
#include <string>

#include <cassert>

namespace DBCpp
{	
	// locales
	namespace Locale
	{
		enum Locale
		{
			enUS = 0,	// american english
			enGB = 0,	// british english
			coKR = 1,	// corean
			frFR = 2,	// french
			deDE = 3,	// german
			zhCN = 4,	// simplified chinese
			zhTW = 5,	// traditional chinese
			esES = 6,	// castilian spanish
			esMX = 6,	// mexican spanish
			ruRU = 7,	// russian
			// there may be 10 more locales... but who knows...
			Default = 0xFFFFFFFF	// default value... also approves that locales is 32 bit
		};
	}

	// the DBC signature
#define DBC_SIGNATURE (0x43424457)
	
	// dbc datatypes
	typedef unsigned int dbc_string[17];
	typedef unsigned int dbc_int;
	typedef unsigned int dbc_bool;
	typedef float dbc_float;
#define DBC_TRUE	0xFFFFFFFF
#define DBC_FALSE	0

	// the DBC header
	struct dbcHeader
	{
		unsigned int signature;			// must always be DBC_SIGNATURE
		unsigned int records;			// number of records
		unsigned int fields;			// number of fields per record
		unsigned int record_size;		// fields * field_size
		unsigned int string_block_size;	// size of the string block, 1 if empty
	};

	// a template class for all DBCs
	template <typename RecordType>
	class templateDBC
	{
	public:
		typedef RecordType Record;
		typedef std::vector<Record> Records;
		typedef typename Records::iterator RecordIterator;
		typedef RecordIterator iterator;
		typedef std::map<unsigned int, std::string> StringTable;
		typedef StringTable::iterator StringTableIterator;
	protected:
		dbcHeader header;
		Records records;
		StringTable strings;
	public:
		templateDBC(void) {}
		virtual ~templateDBC(void) {}
		inline iterator begin(void)
		{
			return records.begin();
		}
		inline iterator end(void)
		{
			return records.end();
		}
		inline Record operator[] (unsigned int index)
		{
			return (records[n]);
		}
		inline dbcHeader getHeader(void)
		{
			return header;
		}
		bool loadFromFile(const std::string& file_name)
		{
			std::ifstream source(file_name.c_str(), std::ios::in | std::ios::binary);
			// file could not be opened
			if (!source)
				return false;
			source.seekg(0, std::ios::end);
			int size = static_cast<int>(source.tellg());
			// file too small
			if (size < sizeof(dbcHeader))
			{
				source.close();
				return false;
			}
			source.seekg(0);
			char *content = new char[size];
			// not enough memory
			if (!content)
			{
				source.close();
				return false;
			}
			source.read(content, size);
			source.close();

			header = *((dbcHeader*)content);

			// wrong signature
			if (header.signature != DBC_SIGNATURE)
			{
				delete[] content;
				return false;
			}

			// again file too small
			if (((header.record_size * header.records) + sizeof(dbcHeader)) + header.string_block_size > (unsigned int)size)
			{
				delete[] content;
				return false;
			}

			// wrong record size
			if (header.record_size != sizeof(Record))
			{
				delete[] content;
				return false;
			}

			// load the records
			for (unsigned int n = 0; n < header.records; ++n)
				records.push_back(((Record*)(&(pHeader[1])))[n]);

			// load the strings
			if (header.string_block_size == 1)	// no string block
				return true;
			unsigned int offset = sizeof(dbcHeader) + (header.record_size * header.records) + 1; // leading 0 character
			std::string strBuf;
			unsigned int strbegin = 1;
			for (unsigned int c = 0; c < (header.string_block_size - 1); ++c)
			{
				if (content[offset + c] == '\0')	// string end
				{
					strings.insert(std::pair<unsigned int, std::string>(strbegin, strBuf));
					strBuf = "";
					strbegin = c + 2;
				}
				else
				{
					strBuf += content[offset + c];
				}
			}
			// clean up
			delete[] content;

			// ok^^
			return true;
		}
		const char* getLocalString(dbc_string field, Locale::Locale loc = Locale::Default)
		{
			if ((loc >= Locale::enUS) && (loc <= Locale::ruRU))
			{
				StringTableIterator found = strings.find(field[loc]);
				if (found != strings.end())
					return ((found->second).c_str());
				else
					return 0;
			}
			// backwards because of the possibility of english and a second locale in one file
			// so we prefer the second local ;-)
			for (unsigned int n = Locale::ruRU; n >= Locale::enUS; --n)
			{
				if (field[n] != 0)
				{
					StringTableIterator found = strings.find(field[n]);
					if (found != strings.end())
						return ((found->second).c_str());
				}
			}
			return 0;
		}
		const char* getNonLocalString(dbc_int str)
		{
			StringTableIterator found = strings.find(str);
				if (found != strings.end())
					return ((found->second).c_str());
				else
					return 0;
		}
	};
}

// the specific DBCs
#include "Records/AreaTable.hpp"
#include "Records/ChrClasses.hpp"
#include "Records/ChrRaces.hpp"
#include "Records/CurrencyTypes.hpp"
#include "Records/Faction.hpp"
#include "Records/FactionTemplate.hpp"
#include "Records/Item.hpp"
#include "Records/ItemBagFamily.hpp"
#include "Records/ItemClass.hpp"
#include "Records/ItemSubClass.hpp"
#include "Records/Map.hpp"
#include "Records/SkillLine.hpp"

#endif // __DBCpp_HPP__
